<?php
namespace app\api\controller;

use app\api\validate\Customer as ValidateCustomer;
use app\common\PasswordHelper;
use EasyWeChat\Factory;
use support\Db;
use support\Redis;
use support\Request;
use support\Response;
use Tinywan\Jwt\JwtToken;
use Webman\Exception\BusinessException;

class CustomerController extends BaseClientController
{

    public $noNeedLogin = ['login', "register", "wxAuthRedirect", "wxAuthCallback", "miniProgramLogin", "miniProgramGetPhone"];

    /**
     * 用户注册
     *
     * 该方法用于处理用户注册请求，接收用户提交的手机号和密码，
     * 检查手机号是否已存在，若不存在则对密码进行加密后存入数据库。
     *
     * @param Request $request 请求对象，包含客户端发送的请求信息
     * @return Response 返回操作结果，注册成功返回包含成功信息的 JSON 响应
     * @throws BusinessException 若手机号已存在，抛出业务异常
     */
    public function register(Request $request): Response
    {
        // 从 POST 请求中获取用户提交的手机号
        $phone = $request->post('phone');
        $code  = $request->post("code");

        // 从 POST 请求中获取用户提交的密码
        $password = $request->post('password');

        // 判断该手机号是否已在 customer 表中存在
        if (Db::table('customer')->where('phone', $phone)->exists()) {
            // 若手机号已存在，抛出业务异常并给出提示信息，错误码为 400
            throw new BusinessException('手机号已存在', 400);
        }

        // redis中获取验证码
        $smsId = Redis::get("mobile_{$phone}");
        // 验证验证码
        if (! $smsId) {
            throw new BusinessException("验证码不存在");
        }
        // 验证验证码
        if ($smsId != $code) {
            throw new BusinessException("验证码错误");
        }

        // 调用 PasswordHelper 类的 hashPassword 方法对用户密码进行加密
        $hashedPassword = PasswordHelper::hashPassword($password);

        // 将加密后的密码和手机号插入到 customer 表中
        Db::table('customer')->insert([
            'phone'    => $phone,
            'password' => $hashedPassword,
        ]);

        // 从redis中移除验证码
        Redis::del("mobile_{$phone}");

        // 注册成功，返回包含成功信息的 JSON 响应
        return $this->success([], "注册成功");
    }

    /**
     * 用户登录
     *
     * 该方法用于处理用户登录请求，接收用户提交的手机号和密码，
     * 验证用户信息是否正确，若正确则生成 JWT Token 并返回。
     *
     * @param Request $request 请求对象，包含客户端发送的请求信息
     * @return Response 返回操作结果，登录成功返回包含 Token 的 JSON 响应
     * @throws BusinessException 若用户名或密码错误，抛出业务异常
     */
    public function login(Request $request): Response
    {
        $uid = $request->uid;
        // 从 POST 请求中获取用户提交的手机号
        $phone = $request->post('phone');

        // 从 POST 请求中获取用户提交的密码
        $password = $request->post('password');

        // 对用户输入的手机号和密码进行验证
        $validate = new ValidateCustomer();

        if (! $validate->check(['phone' => $phone, 'password' => $password])) {
            throw new BusinessException($validate->getError(), 400);
        }

        // 从数据库获取指定手机号的用户信息
        $user = Db::table('customer')->where('phone', $phone)->first();

        // 检查用户信息是否存在
        if (! $user) {
            // 若用户信息不存在，抛出业务异常并给出提示信息，错误码为 401
            throw new BusinessException('用户名或密码错误', 401);
        }

        // 验证用户输入的密码与数据库中存储的加密密码是否匹配
        if (PasswordHelper::verifyPassword($password, $user->password)) {
            // 若密码验证通过，生成 JWT Token，传入用户 ID
            $token = JwtToken::generateToken(['id' => $user->id]);
            // 将token存入redis
            Redis::set('token:' . $user->id, $token['access_token']);
            // 登录成功，返回包含成功信息和 Token 的 JSON 响应
            return json(['code' => 200, 'message' => '登录成功', 'token' => $token['access_token']]);
        } else {
            // 若密码验证失败，抛出业务异常并给出提示信息，错误码为 401
            throw new BusinessException('用户名或密码错误', 401);
        }
    }

    /**
     * 引导用户到微信授权页面
     *
     * @param Request $request 请求对象
     * @return Response 重定向响应，将用户导向微信授权页面
     */
    public function wxAuthRedirect(Request $request): Response
    {
        $app         = Factory::officialAccount(config("wechat"));
        $redirectUrl = $app->oauth->scopes(['snsapi_userinfo'])->redirect();
        return redirect($redirectUrl);
    }

    /**
     * 处理微信授权回调
     *
     * @param Request $request 请求对象
     * @return Response 返回包含用户信息和 Token 的 JSON 响应
     */
    public function wxAuthCallback(Request $request): Response
    {

        // 获取code
        $code = $request->get('code');
        $app  = Factory::officialAccount(config("wechat"));
        $user = $app->oauth->userFromCode($code);
        // 根据微信 openid 查找用户
        $customer = Db::table('customer')->where('wechat_openid', $user->getId())->first();

        if (! $customer) {
            // 若用户不存在，自动注册新用户
            $hashedPassword = PasswordHelper::hashPassword("123456");
            $customerId     = Db::table('customer')->insertGetId([
                'wechat_openid' => $user->getId(),
                'nickname'      => $user->getNickname(),
                'password'      => $hashedPassword,
            ]);
            $customer = (object) [
                'id'            => $customerId,
                'wechat_openid' => $user->getId(),
                'nickname'      => $user->getNickname(),
            ];
        }

        // 生成 JWT Token
        $token = JwtToken::generateToken(['id' => $customer->id]);
        return $this->success(['token' => $token['access_token']]);
    }

    /**
     * 微信小程序登录
     *
     * @param Request $request 请求对象，包含小程序端传来的 code
     * @return Response 返回包含用户信息和 Token 的 JSON 响应
     * @throws BusinessException 若获取用户信息失败，抛出业务异常
     */
    public function miniProgramLogin(Request $request): Response
    {
        $code = $request->post('code');
        if (! $code) {
            throw new BusinessException('缺少 code 参数', 400);
        }

        $app    = Factory::miniProgram(config('wechat.mini_program'));
        $result = $app->auth->session($code);

        if (isset($result['errcode'])) {
            throw new BusinessException('获取 session 失败: ' . $result['errmsg'], 400);
        }

        $openid = $result['openid'];

        // 根据 openid 查找用户
        $customer = Db::table('customer')->where('wechat_openid', $openid)->first();

        if ($customer) {
            // 用户存在，生成 JWT Token
            $token = JwtToken::generateToken(['id' => $customer->id]);
            return $this->success(['token' => $token['access_token']]);
        }

        // 用户不存在，返回需要获取手机号的提示
        return $this->error("需要获取手机号完成注册", 201);
    }

    /**
     * 微信小程序获取手机号完成注册
     *
     * @param Request $request 请求对象，包含 encryptedData 和 iv，以及 session_key 和 openid
     * @return Response 返回包含用户信息和 Token 的 JSON 响应
     * @throws BusinessException 若解密手机号失败或注册失败，抛出业务异常
     */
    public function miniProgramGetPhone(Request $request): Response
    {

        $code = $request->post("code");
        if (! $code) {
            throw new BusinessException('缺少 code 参数', 400);
        }

        $app       = Factory::miniProgram(config('wechat.mini_program'));
        $phoneData = $app->phone_number->getUserPhoneNumber($code);
        $phone     = $phoneData['phoneNumber'];
        // 判断手机号是否已存在
        if (Db::table('customer')->where('phone', $phone)->exists()) {
            throw new BusinessException('手机号已存在', 400);
        }

        // 注册新用户
        $hashedPassword = PasswordHelper::hashPassword("123456");

        $customerId = Db::table('customer')->insertGetId([
            'phone'    => $phone,
            'password' => $hashedPassword,
        ]);

        $customer = (object) [
            'id'    => $customerId,
            'phone' => $phone,
        ];

        // 生成 JWT Token
        $token = JwtToken::generateToken(['id' => $customer->id]);

        return json([
            'code'      => 200,
            'message'   => '微信小程序注册登录成功',
            'token'     => $token['access_token'],
            'user_info' => [
                'id'            => $customer->id,
                'wechat_openid' => $customer->wechat_openid,
                'phone'         => $customer->phone,
            ],
        ]);
    }

    /**
     * 获取用户信息
     *
     * 该方法根据传入的用户 ID 从数据库中查询对应的用户信息，
     * 并将查询结果作为成功响应返回。
     *
     * @param Request $request 请求对象，包含客户端发送的请求信息，期望包含用户 ID
     * @return Response 返回操作结果，包含用户信息的成功响应
     */
    public function getCustomerInfo(Request $request): Response
    {
        // 从请求对象中获取用户 ID
        $uid = $request->uid;
        // 根据用户 ID 从 customer 表中查找对应的用户信息
        $customer = Db::table('customer')->find($uid);
        // 返回包含用户信息的成功响应
        return $this->success($customer);
    }
}
